
#ifndef runtime_h
#define runtime_h


#define PIXIE_DEBUG // comment or delete this line out to supress sending of runtime messages to the Serial Monitor 

// include basic serial commands library ( 16-bit integer commands (with CR) from PC serial console)
#include "Messenger.h" 

// fast screen clear routine - actually a *SHADER PROGRAM* running on the J1 coprocessor, not the Arduino !! )
// to be called like this: GD.microcode(eraser_code, sizeof(eraser_code));
#include "eraser.h" 

// SHADER PROGRAM for fast line drawing
#include "wireframe.h"

// #include "Adafruit_GFX.h"


// exported function declarations

void waitready();
void sleepNow(); // this is currently NOT WORKING with the GAMEDUINO SHIELD attached !! -- NEEDS INVESTIGATION !!
void clear_screen();
byte replicate(byte color);
void line(byte x0, byte y0, byte x1, byte y1, byte color);
void line_accel(byte x0, byte y0, byte x1, byte y1, byte color);
void setpixel(byte x, byte y, byte color);
static uint16_t GET_VMEM_ADDRESS(byte x, byte y);


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////// the following section defines USER SW Callback methods //////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C" 
{
  // generic user callback function
  typedef void (*UserSW_Callback)(void);
}

extern "C" 
{
  // user callback function #1
  typedef void (*UserSW_1_Setup_Function)(void);
  typedef void (*UserSW_1_Loop_Function)(void);
}

extern "C" 
{
  // user callback function #2
  typedef void (*UserSW_2_Setup_Function)(void);
  typedef void (*UserSW_2_Loop_Function)(void);
}

extern "C" 
{
  // user callback function #3
  typedef void (*UserSW_3_Setup_Function)(void);
  typedef void (*UserSW_3_Loop_Function)(void);
}

extern "C" 
{
  // user callback function #4
  typedef void (*UserSW_4_Setup_Function)(void);
  typedef void (*UserSW_4_Loop_Function)(void);
}

extern "C" 
{
  // user callback function #5
  typedef void (*UserSW_5_Setup_Function)(void);
  typedef void (*UserSW_5_Loop_Function)(void);
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////


// messenger class
Messenger PIXIE_COMMAND;

// generic drawing class from Adafruit
// Adafruit_GFX PIXIE_DRAW;

// this is set TRUE when the PIXYBIT timer interrupt occurs. can be used by SW to do certain things "once in a while" :)
// boolean TIMER_EXPIRED = false;

// position in the EEPROM file of the last executed command ( saved on command #99 )
#define EEPROM_LAST_MSG 27

#define RANDOM_RGB() RGB(random(256),random(256),random(256))

#define SWAP(a, b) (swap = (a), (a) = (b), (b) = swap)

// read serial commands as integers, Carriage Return delimitates commands
// #define PIXIE_SEPARATOR 13 

// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

#define BLACK RGB(0,0,0)
#define WHITE RGB(255,255,255)

#define PIXIE_BAUDRATE 9600

// please note that PINS 13, 12, 11, and 10 are used by SPI, and implicitly by the GAMEDUINO BOARD !!
#define PIXIE_ATTENTION_LED 2
#define PIXIE_BUTTON_A      4 // please wire this up in PULL-DOWN configuration !! ( so digitalRead() returns TRUE when pressed )
#define PIXIE_BUTTON_B      7 // please wire this up in PULL-DOWN configuration !! ( so digitalRead() returns TRUE when pressed )

#define PIXIE_TILT_SENSOR   8 // please note that Pin2 also serves as Interrupt0 !!!

// definition of error codes
#define ERR_OK                                 0
#define ERR_BAD_COMMAND                        1 
#define ERR_MISSING_GAMEDUINO_SHIELD           3

// command that was received from Serial ( PC diagnosis )
int READ_SERIAL;

// this is used by command #99 -> to save last issued command to EEPROM
// it is set to command #0 by default, which indicates NO COMMAND
int SAVE_SERIAL = 0; 


int ARDUINO_COMMAND     = 0; // no command for the ARDUNIO
int GAMEDUINO_COMMAND   = 0; // no command for the GAMEDUINO
bool SERIAL_COMM         = false; // no incoming SERIAL communication
bool SOMETHING_TO_SAY    = false; // nothing to communicate back via SERIAL line

bool GAMEDUINO_READY = false;

// this is used for asynchronous messaging with the PC for debug purposes
String message_out;

// X*Y coordinates, used to calculate phsyical screen addresses on the gameduino shield
int X = 0; 
int Y = 0;

// just a generic ON/OFF flag
boolean IT_IS_ON = false;

// error handling
byte ERR = 0;
bool FAIL = false;

byte flip;
byte plotting;

byte color;

long time = 0; // variable used to count debounce for tilt switches
long debounce = 50; // debounce timer for tilt sensor

// the pixybit runtime supports up to 6 timers

unsigned long PIXIE_TIMER1; // 10ms
unsigned long PIXIE_TIMER2; // 250ms
unsigned long PIXIE_TIMER3; // 500ms
unsigned long PIXIE_TIMER4; // 1s
unsigned long PIXIE_TIMER5; // 100ms
unsigned long PIXIE_TIMER6; // 15ms

// checked in main loop for timer-conditioned execution
boolean       PIXIE_TIMER1_EXPIRED = false;
boolean       PIXIE_TIMER2_EXPIRED = false;
boolean       PIXIE_TIMER3_EXPIRED = false;
boolean       PIXIE_TIMER4_EXPIRED = false;
boolean       PIXIE_TIMER5_EXPIRED = false;
boolean       PIXIE_TIMER6_EXPIRED = false;

// holds true for the time buttons A or B are pressed
boolean PIXIE_BUTTON_A_PRESSED     = false;
boolean PIXIE_BUTTON_B_PRESSED     = false;

boolean PIXIE_SENSOR_TILTED        = false;

boolean PREVIOUS_TILT, CURRENT_TILT;

// used by the PIXYBIT runtime to control Vertical sync ( 20 = ON, 21 = OFF / default ) 
boolean VSYNC = false; 

// used by gameduino user space sw to determine wether to Clear the screen or not. ( 11 = CRSCR, 12 = NO CRSCR)
boolean CLRSCR = false; 

// used by gameduino user space sw to force "HW accelerated" drawing algos ( 13 = HW / 14 SW algo )
boolean SHADER_PROGRAMS = false; 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////// user software HOOK FUNCTIONS //////////////////////////////////
UserSW_Callback callback_Setup1   = NULL;
UserSW_Callback callback_Loop1    = NULL;

UserSW_Callback callback_Setup2   = NULL;
UserSW_Callback callback_Loop2    = NULL;

UserSW_Callback callback_Setup3   = NULL;
UserSW_Callback callback_Loop3    = NULL;

UserSW_Callback callback_Setup4   = NULL;
UserSW_Callback callback_Loop4    = NULL;

UserSW_Callback callback_Setup5   = NULL;
UserSW_Callback callback_Loop5    = NULL;

void attach_user_Setup1(UserSW_Callback newFunction) 
{
  callback_Setup1 = newFunction;
}

void attach_user_Loop1(UserSW_Callback newFunction) 
{
  callback_Loop1 = newFunction;
}

void attach_user_Setup2(UserSW_Callback newFunction) 
{
  callback_Setup2 = newFunction;
}

void attach_user_Loop2(UserSW_Callback newFunction) 
{
  callback_Loop2 = newFunction;
}

void attach_user_Setup3(UserSW_Callback newFunction) 
{
  callback_Setup3 = newFunction;
}

void attach_user_Loop3(UserSW_Callback newFunction) 
{
  callback_Loop3 = newFunction;
}

void attach_user_Setup4(UserSW_Callback newFunction) 
{
  callback_Setup4 = newFunction;
}

void attach_user_Loop4(UserSW_Callback newFunction) 
{
  callback_Loop4 = newFunction;
}

void attach_user_Setup5(UserSW_Callback newFunction) 
{
  callback_Setup5 = newFunction;
}

void attach_user_Loop5(UserSW_Callback newFunction) 
{
  callback_Loop5 = newFunction;
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////


void setup_user_space_software()
{

  if (callback_Setup1 != NULL) 
  {
    (*callback_Setup1)();
    Serial.println(F("User Space SW 1 Initialized")); // using F(.. does not copy the String into SRAM (2K total ), but use FLASH 
  }
  
  if (callback_Setup2 != NULL) 
  {
    (*callback_Setup2)();
    Serial.println(F("User Space SW 2 Initialized"));
  }
  
  if (callback_Setup3 != NULL) 
  {
    (*callback_Setup3)();
    Serial.println(F("User Space SW 3 Initialized"));
  }  
  
  if (callback_Setup4 != NULL) 
  {
    (*callback_Setup4)();
    Serial.println(F("User Space SW 4 Initialized"));
  }  
  
  if (callback_Setup5 != NULL) 
  {
    (*callback_Setup5)();
    Serial.println(F("User Space SW 5 Initialized"));
  }
  

}

// convert X by Y coordinate into physical video memory address
static uint16_t GET_VMEM_ADDRESS(byte x, byte y)
{
  return RAM_PIC + 64 * y + x;
}

// for shader program to finish
void waitready()
{
  while (GD.rd(COMM+7))
    ;
}

// replicate a 2-bit color across the whole byte.  Optimization for setpixel
byte replicate(byte color)
{
  return (color << 6) | (color << 4) | (color << 2) | color;
}

void clear_screen()
{
  // Clear the screen
  // 

  // use fast SHADER PROGRAM on the J1 Forth Co-Processor to clear the screen

    //byte color = flip ? 1 : 2;

  // use HW accelerated screen blank, or use SW screen blank
  if (SHADER_PROGRAMS)
  {
    plotting = 0;
    GD.wr(J1_RESET, 1);
    GD.wr(COMM+7, 1);
    GD.wr(COMM+8, replicate(color ^ 3));
    GD.microcode(eraser_code, sizeof(eraser_code));
  }
  else
  {
    GD.fill(RAM_SPRIMG, 0, 16384);
  }
}


// Set pixel at (x,y) to color.  (note that color is replicated).
void setpixel(byte x, byte y, byte color)
{
  /*
   Because of the way the sprites are laid out in setup(), it's not too
   hard to translate the pixel (x,y) to an address and mask.  Taking the
   two byte values as x7-x0 and y7-y0, the address of the pixel is:
   
   x5 x4 y7 y6 y5 y4 y3 y2 y1 y0 x3 x2 x1 x0
   
   (x6, x7) gives the value of the mask.
   */
  unsigned int addr = RAM_SPRIMG | (x & 0xf) | (y << 4) | ((x & 0x30) << 8);
  byte mask = 0xc0 >> ((x >> 5) & 6);
  GD.wr(addr, (GD.rd(addr) & ~mask) | (color & mask));
}

// Draw color line from (x0,y0) to (x1,y1).
void line(byte x0, byte y0, byte x1, byte y1, byte color)
{
  byte swap;
  color = replicate(color);
  byte steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    SWAP(x0, y0);
    SWAP(x1, y1);
  }
  if (x0 > x1) {
    SWAP(x0, x1);
    SWAP(y0, y1);
  }
  int deltax = x1 - x0;
  int deltay = abs(y1 - y0);
  int error = deltax / 2;
  char ystep;
  if (y0 < y1)  
    ystep = 1;
  else
    ystep = -1;
  byte x;
  byte y = y0;
  for (x = x0; x < x1; x++) {
    if (steep)
      setpixel(y, x, color);
    else
      setpixel(x, y, color);
    error -= deltay;
    if (error < 0) {
      y += ystep;
      error += deltax;
    }
  }
}

// accelerated version of the line drawing alogorythm
void line_accel(byte x0, byte y0, byte x1, byte y1, byte color)
{
  byte swap;
  
  byte steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    SWAP(x0, y0);
    SWAP(x1, y1);
  }
  if (x0 > x1) {
    SWAP(x0, x1);
    SWAP(y0, y1);
  }
  int deltax = x1 - x0;
  int deltay = abs(y1 - y0);
  int error = deltax / 2;
  char ystep;
  if (y0 < y1)  
    ystep = 1;
  else
    ystep = -1;
  byte x;
  byte y = y0;

  waitready();
  if (!plotting) {
    GD.microcode(wireframe_code, sizeof(wireframe_code));
    plotting = 1;
    // byte color = flip ? 1 : 2;
    GD.wr(COMM+8, color << 6);
  }
  GD.__wstart(COMM+0);
  SPI.transfer(x0);
  SPI.transfer(y0);
  SPI.transfer(x1);
  SPI.transfer(y1);
  SPI.transfer(steep);
  SPI.transfer(deltax);
  SPI.transfer(deltay);
  SPI.transfer(ystep);
  GD.__end();
}


// Define messenger function
void messageCompleted() 
{
  Serial.println("*");

  if (PIXIE_COMMAND.available())
  {
    READ_SERIAL = PIXIE_COMMAND.readInt();
    SERIAL_COMM = true;
  }

  static char last_msg[30];

  if (GAMEDUINO_READY)
  {
    GD.putstr(0, 1, "                             ");
    sprintf(last_msg, "LAST MSG : %d", READ_SERIAL );
    GD.putstr(0, 1, last_msg);
  }
}

void SHOW_FPS_COUNTER()
{

  static byte every;

  if (++every == 4) 
  {
    static long tprev;
    long t = micros();
    every = 0;

    static char msg[30];
    int fps10 = int(4 * 10000000UL / (t - tprev));
  
    // only show the FPS once in a while, so we can actually read the value ;)
    if (PIXIE_TIMER5_EXPIRED) 
    {
      sprintf(msg, "%3d.%d fps  ", fps10 / 10, fps10 % 10);
      GD.putstr(40, 1, msg);
    }
    
    tprev = t;
  }

}


void pixie_startup()
{
  Serial.println();
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>> PIXYBIT is STARTING UP..>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println();
}

void pixie_ready()
{
  Serial.println();
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>> PIXYBIT is NOW READY.>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println();
}



boolean gameduino_get_status()
{
  Serial.println("************************");  
  Serial.print  ("IDENT:    ");
  Serial.println(GD.rd(IDENT), HEX);
  Serial.print  ("REV:      ");
  Serial.println(GD.rd(REV), HEX);
  Serial.println("************************");

  if (0x6D != GD.rd(IDENT)) return false; // no gameduino board is present

  return true;
}

void bring_up_the_gameduino_shield()
{

  int i;

  GD.begin();

  if (gameduino_get_status())
  {

    GD.ascii(); // Load the standard ascii font

    // SETUP 2D surface as a sprite, 256x256 pixels
    // this will be used by per-pixel drawing operations
    // First 64 use bits 0-1, next 64 use bits 2-4, etc.
    // This gives a 256 x 256 4-color bitmap.

    for (i = 0; i < 256; i++) 
    {
      int x =     72 + 16 * ((i >> 4) & 15);
      int y =     22 + 16 * (i & 15);
      int image = i & 63;     /* image 0-63 */
      int pal =   3 - (i >> 6);   /* palettes bits in columns 3,2,1,0 */
      GD.sprite(i, x, y, image, 0x8 | (pal << 1), 0);
    }

    clear_screen(); // clear the sreen
    
    GD.putstr(0, 0, "PIXYBIT runtime");

    GAMEDUINO_READY = true;

    GD.wr16(BG_COLOR, RGB(0, 0, 64));   // relaxing dark blue background

    Serial.println(">>> GAMEDUINO Shield is NOW UP !!");
  }
  else
  {
    GAMEDUINO_READY = false;

    Serial.println(">>> GAMEDUINO Shield is NOT PRESENT !!");
    GD.end();
  }

}

byte pixie_setup()
{
  ERR = ERR_OK; // RESET ERROR CODE to "NO ERROR"

  // initialize the random number generator
  randomSeed(analogRead(0));

  Serial.begin(PIXIE_BAUDRATE);
  pinMode(PIXIE_ATTENTION_LED, OUTPUT);

  // initiate serial command processor 
  PIXIE_COMMAND =  Messenger();
  PIXIE_COMMAND.attach(messageCompleted);

  pixie_startup();

  bring_up_the_gameduino_shield();
  return ERR;
}

// this is currently NOT WORKING with the GAMEDUINO SHIELD attached !!
void sleepNow()
{
    // Choose our preferred sleep mode:
    set_sleep_mode(SLEEP_MODE_IDLE);
 
    // Set sleep enable (SE) bit:
    sleep_enable();
 
    // Put the device to sleep:
    sleep_mode();
 
    // Upon waking up, sketch continues from this point.
    sleep_disable();
}


byte pixie_shutdown()
{
  Serial.println();
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>> PIXYBIT is SHUTTING DOWN..>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>> PLEASE PRESS HW RESET BUTTON to RESTART >>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println(F(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"));
  Serial.println();

  do {
  } 
  while(1); // waiting for reset button to be pressed

}

void loop_reset_timers()
{
  PIXIE_TIMER1_EXPIRED = false;
  PIXIE_TIMER2_EXPIRED = false;
  PIXIE_TIMER3_EXPIRED = false;
  PIXIE_TIMER4_EXPIRED = false;
  PIXIE_TIMER5_EXPIRED = false;
  PIXIE_TIMER6_EXPIRED = false;
}

void loop_check_timers()
{
  
  if ((millis() - PIXIE_TIMER1) >= 10UL)
  {
  // the 10ms timer has expired
  PIXIE_TIMER1_EXPIRED = true;
  // restart timer
  PIXIE_TIMER1 = millis();
  //
  }
  
  if ((millis() - PIXIE_TIMER2) >= 250UL)
  {
  // the 250ms timer has expired
  PIXIE_TIMER2_EXPIRED = true;
  // restart timer
  PIXIE_TIMER2 = millis();
  //
  }
  
  if ((millis() - PIXIE_TIMER3) >= 500UL)
  {
  // the 500ms timer has expired
  PIXIE_TIMER3_EXPIRED = true;
  // restart timer
  PIXIE_TIMER3 = millis();
  //
  }
  
  if ((millis() - PIXIE_TIMER4) >= 1000UL)
  {
  // the 1 second timer has expired
  PIXIE_TIMER4_EXPIRED = true;
  // restart timer
  PIXIE_TIMER4 = millis();
  //
  }
  
  if ((millis() - PIXIE_TIMER5) >= 100UL)
  {
  // the 100ms timer has expired
  PIXIE_TIMER5_EXPIRED = true;
  // restart timer
  PIXIE_TIMER5 = millis();
  //
  }
  
  if ((millis() - PIXIE_TIMER6) >= 5UL)
  {
  // the 5ms timer has expired
  PIXIE_TIMER6_EXPIRED = true;
  // restart timer
  PIXIE_TIMER6 = millis();
  //
  }
  
}

void loop_reset_buttons()
{
PIXIE_BUTTON_A_PRESSED = false;
PIXIE_BUTTON_B_PRESSED = false;
}


void loop_reset_sensors()
{
  PIXIE_SENSOR_TILTED = false;
}

void loop_check_sensors()
{  
 
  CURRENT_TILT = digitalRead(PIXIE_TILT_SENSOR);
 
  // If the switch changed, due to bounce or pressing...
  if (CURRENT_TILT != PREVIOUS_TILT) 
  {  
   
    if ((millis() - time) > debounce) 
    {     
      #ifdef PIXIE_DEBUG
          Serial.println("SENSOR TILTED !! ");
      #endif 
      PIXIE_SENSOR_TILTED = true;
    }
    
    // reset the debouncing timer
    time = millis();

    #ifdef PIXIE_DEBUG
    Serial.println(time);
    #endif

  } 
 
  // Save the last reading so we keep a running tally
  PREVIOUS_TILT = CURRENT_TILT;

}

void loop_check_buttons()
{
    if (digitalRead(PIXIE_BUTTON_A))
    {
      #ifdef PIXIE_DEBUG
      Serial.println("BTN-A ");
      #endif 
      PIXIE_BUTTON_A_PRESSED = true;
    }

    if (digitalRead(PIXIE_BUTTON_B))
    {
      #ifdef PIXIE_DEBUG
      Serial.println("BTN-B ");
      #endif
      PIXIE_BUTTON_B_PRESSED = true;
    }    
}

void loop_listen_comm()
{
  while ( Serial.available() ) 
  {
    PIXIE_COMMAND.process( Serial.read() );
    #ifdef PIXIE_DEBUG
    Serial.println(".");
    #endif
  }
}

void restart_user_space_sw()
{
  if (callback_Setup1 != NULL) 
  {
    (*callback_Setup1)();
    Serial.println(F("User Space SW 1 Initialized"));
  }

  if (callback_Setup2 != NULL) 
  {
    (*callback_Setup2)();
    Serial.println(F("User Space SW 2 Initialized"));
  }

  if (callback_Setup3 != NULL) 
  {
    (*callback_Setup3)();
    Serial.println(F("User Space SW 3 Initialized"));
  }  

  if (callback_Setup4 != NULL) 
  {
    (*callback_Setup4)();
    Serial.println(F("User Space SW 4 Initialized"));
  }  

  if (callback_Setup5 != NULL) 
  {
    (*callback_Setup5)();
    Serial.println(F("User Space SW 5 Initialized"));
  }

}

void loop_interpret_comm()
{

  // debug
  #ifdef PIXIE_DEBUG
  Serial.println();
  Serial.print  (F("Received this command: "));
  Serial.println(READ_SERIAL);
  /// debug
  #endif

  switch(READ_SERIAL)
  {

    // init random seed 
    randomSeed(analogRead(0));

  case 200:
  {
  // start 250ms task test ( blinks LED13 )
  // please note that the GAMEDUINO loop keeps doing whatever it does at the moment
  // as it's basically governed by the GAMEDUINO_COMMAND !!
  ARDUINO_COMMAND = READ_SERIAL;
  }; break;
  
  case 201:
  {
  // stop 250ms task ( stops LED13 blinking )
  ARDUINO_COMMAND = READ_SERIAL;
  }; break;

  // sleep
  case 100:
  {
      Serial.println("GOOD NIGHT !!");
      delay(100);
      sleepNow();  
}; break;
  // wake up:
  case 101:
  {
      delay(100);
      Serial.println("GOOD MORNING !!");
  }; break;
  
  case 99:
    { 
      
    EEPROM.write(0, 27); // validate EEPROM ( value 27 is "by contract" )  
    // save last issued command to EEPROM
    EEPROM.write(EEPROM_LAST_MSG, SAVE_SERIAL);
    // restore initial command and allow transparent execution of previous command
    READ_SERIAL = SAVE_SERIAL; 
    
    // save the environment variables
    EEPROM.write(EEPROM_LAST_MSG + 1, VSYNC);
    EEPROM.write(EEPROM_LAST_MSG + 2, CLRSCR);
    EEPROM.write(EEPROM_LAST_MSG + 3, SHADER_PROGRAMS);
    // .. add here as necessary, and make sure these are correctly restored at uC startup

    };
    break;

  case 1:
    {
      // gameduino selftest
      GAMEDUINO_COMMAND = READ_SERIAL; 
    }; 
    break;
  case 2:
    {
      // stop selftest
      GAMEDUINO_COMMAND = READ_SERIAL;
      SOMETHING_TO_SAY = true;
      message_out = ">>> continuous self-test has been stopped !! \n";
    }; 
    break;
  case 3:
    {
      // 
    }; 
    break;
  case 4:
    {
      // 
    }; 
    break;  
  case 5:
    {
      // flash led 13 ( see if board is alive ) 
      ARDUINO_COMMAND = READ_SERIAL;   
    }; 
    break;
  case 6:
    {
      ARDUINO_COMMAND = READ_SERIAL; 
    }; 
    break;
  case 7:
    {
      // clear screen
      GAMEDUINO_COMMAND = READ_SERIAL;
      SOMETHING_TO_SAY = true;
      message_out = ">>> CLEAR SCREEN command has been issued !!\n";
    }; 
    break;
  case 8:
    {
      // start user space SW
      GAMEDUINO_COMMAND = READ_SERIAL;
      SOMETHING_TO_SAY = true;
      message_out = ">>> USER SPACE SOFTWARE EXECUTION STARTED !!\n";
    }; 
    break;
  case 9:
    {
      // stop user space SW
      GAMEDUINO_COMMAND = READ_SERIAL;
      SOMETHING_TO_SAY = true;
      message_out = ">>> [STOPPED]: USER SPACE SOFTWARE EXECUTION.\n";
    }; 
    break;

  case 10:
    {
      // restart user space program
      restart_user_space_sw();

      GAMEDUINO_COMMAND = 0;
      ARDUINO_COMMAND = 0;
      SOMETHING_TO_SAY = true;
      message_out = ">>> [ RESET ]: USER SPACE SOFTWARE EXECUTION has been RE-INITIALIZED.\n";
    }; 
    break;

  case 11:
    {
      // set clear screen between frames
      GAMEDUINO_COMMAND = READ_SERIAL;    
    }; 
    break;

  case 12:
    {
      // don't clear screen between frames
      GAMEDUINO_COMMAND = READ_SERIAL;    
    }; 
    break;

  case 13:
    {
      SHADER_PROGRAMS = true; // enable HW accelerated line drawing
    }; 
    break;

  case 14:
    {
      SHADER_PROGRAMS = false; // disable HW accelerated line drawinf
    }; 
    break; 


  case 20:
    {
      VSYNC = true; // enable HW accelerated line drawing
    }; 
    break;

  case 21:
    {
      VSYNC = false; // disable HW accelerated line drawinf
    }; 
    break; 

  default: 
    ERR = ERR_BAD_COMMAND; 
    break; // invalid command failure
  }
}

void loop_arduino()
{

  if (200 == ARDUINO_COMMAND)
  {
     if (PIXIE_TIMER2_EXPIRED)
     {
        if (IT_IS_ON)
          {
            digitalWrite(PIXIE_ATTENTION_LED, LOW); 
            IT_IS_ON = false;
          }
          else
          {
            digitalWrite(PIXIE_ATTENTION_LED, HIGH);
            IT_IS_ON = true;
          }
     }
  }

  if (201 == ARDUINO_COMMAND)
  {
    // turn it off
    digitalWrite(PIXIE_ATTENTION_LED, LOW); 
    ARDUINO_COMMAND = 0;
  }

  if (5 == ARDUINO_COMMAND) // turn on blinking.. see if main message loop is alive
  {
    digitalWrite(PIXIE_ATTENTION_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(100);               // wait for a second
    digitalWrite(PIXIE_ATTENTION_LED, LOW);    // turn the LED off by making the voltage LOW
    delay(100);
  }  

  if (6 == ARDUINO_COMMAND)
  {
    digitalWrite(PIXIE_ATTENTION_LED, LOW);    // turn the LED off by making the voltage LOW
    #ifdef PIXIE_DEBUG
    Serial.println();
    Serial.println(F("Blink test stopped"));
    #endif
    ARDUINO_COMMAND = 0;
  }  

}

void loop_gameduino()
{

  if (!GAMEDUINO_READY)
  {
    ERR = ERR_MISSING_GAMEDUINO_SHIELD;
    GAMEDUINO_COMMAND = 0;
    goto EXIT;
  }

  // display the FPS counter, top-right corner.
  SHOW_FPS_COUNTER();

  // wait for vertical sync.. ( command 20 from serial comm. - off by default / command no. 21 )
  if (VSYNC) GD.waitvblank();

  // call user space loop methods ( functionality is stacked !! - all configured user space SW is executed !! )
  if (8 == GAMEDUINO_COMMAND) // start and continue running user-space SW
  {

    // this flag is set by commands 11 and 12 
    if (true == CLRSCR) 
    {
      clear_screen();
      waitready(); 
    }

    if (callback_Loop1 != NULL) // loop user space SW #1
    {
      (*callback_Loop1)();
    }

    if (callback_Loop2 != NULL) 
    {
      (*callback_Loop2)();
    }   

    if (callback_Loop3 != NULL) 
    {
      (*callback_Loop3)();
    }    

    if (callback_Loop4 != NULL) 
    {
      (*callback_Loop4)();
    }

    if (callback_Loop5 != NULL) 
    {
      (*callback_Loop5)();
    }    

  }

  /* // this implementation is now obsolete
   if (8 == GAMEDUINO_COMMAND) // start and continue running user-space SW
   {
   loop_user_1(); // execute user-space SW !!
   clear_screen();
   // GD.putstr(0, 10, "PIXYBIT runtime");
   }
   */

  if (9 == GAMEDUINO_COMMAND) // stop user space SW
  {    
    GAMEDUINO_COMMAND = 0; // we no longer need this command to be set
    clear_screen();
    // GD.putstr(0, 10, "PIXYBIT runtime");
  }

  if (7 == GAMEDUINO_COMMAND) // clear the graphical screen
  {
    GAMEDUINO_COMMAND = 0; // we no longer need this command to be set
    clear_screen();
    // GD.putstr(0, 10, "PIXYBIT runtime");
  }

  if (2 == GAMEDUINO_COMMAND) // stop continuous selftest
  {
    GAMEDUINO_COMMAND = 0; // stopping the continuous selftest
    clear_screen();
    goto EXIT;
  }  

  if (11 == GAMEDUINO_COMMAND) // continuous clar screen
  {
    CLRSCR = true;
    GAMEDUINO_COMMAND = 8; // continue rendering !!
  }  

  if (12 == GAMEDUINO_COMMAND) // stop continuous clar screen
  {
    CLRSCR = false;
    GAMEDUINO_COMMAND = 8; // continue rendering ;)
  }  


  if (1 == GAMEDUINO_COMMAND) // run continuous selftest
  {


    // Choose a random palette
    GD.wr16(PALETTE4A, RGB(0,0,0));
    GD.wr16(PALETTE4A + 2, RANDOM_RGB());
    GD.wr16(PALETTE4A + 4, RANDOM_RGB());
    GD.wr16(PALETTE4A + 6, RANDOM_RGB());

    X = random(0, 50);
    Y = random(0, 35);
    GD.putstr(X, Y, "PIXYBIT runtime");
  }

EXIT:
  return;   
}

void loop_speak_comm()
{
  Serial.println(message_out);
}

void check_for_error()
{
  if (ERR) 
  {
    Serial.println();
    Serial.print  ("ERR: ");
    Serial.println(ERR);

    if (ERR_BAD_COMMAND == ERR)
    {
      Serial.print  (F("Bad command received via Serial: "));
      Serial.println(READ_SERIAL);
      Serial.println();
      ERR = 0; // we no longer need this error set
      return; // it is NOT necessary to shut down the uC for this !!
    }

    if (ERR_MISSING_GAMEDUINO_SHIELD == ERR)
    {
      Serial.println(F("GAMEDUINO SHIELD IS NOT PRESENT"));
      Serial.println(F("Please power off Arduino board, connect the Gameduino shield, then power on the PIXYBIT !!"));
      Serial.println();
      ERR = 0; // we no longer need this error set
      return; // it is NOT necessary to shut down the uC for this !!
    }

    pixie_shutdown();
  }  

  if (FAIL) 
  {
    Serial.println();
    Serial.print  ("FAIL ");

    pixie_shutdown();
  }
}


//
//
//


#endif

